package br.edu.ufcg.lsd.wbis.control.equipment;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.Date;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Transient;

import org.hibernate.Hibernate;
import org.hibernate.annotations.CollectionOfElements;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;


/**
 * This class represents an inventory equipment
 * @author Paulo Ricardo
 *
 */

@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@OnDelete(action=OnDeleteAction.CASCADE)
public class Equipment {

	@Id
	private String inventoryCode;
	
	private String brand;
	
	private String serialCode;
	
	private Date acquisitionDate;
	
	private String model;
	
	private String localization;
	
	private byte[] receipt;
	
	private boolean alocable;
	
	private boolean available;

	private String type;
	
	@CollectionOfElements
	private Set<String> macAdresses;
	
	@CollectionOfElements
	private Set<String> ipAdresses;
	
	@CollectionOfElements
	private Set<String> hostNames;

	private boolean predefined;
	
	protected Equipment(){}
	

	/**
	 * 
	 * @param inventoryCode
	 * @param brand
	 * @param serialCode
	 * @param acquisitionDate
	 * @param model
	 * @param localization
	 * @param receipt
	 * @param alocable
	 * @param available
	 * @param type
	 */
	public Equipment(String inventoryCode, String brand, String serialCode,
			Date acquisitionDate, String model, String localization,
			byte[] receipt, boolean alocable, boolean available, boolean predefined, String type) {
		this.inventoryCode = inventoryCode;
		this.brand = brand;
		this.serialCode = serialCode;
		this.acquisitionDate = acquisitionDate;
		this.model = model;
		this.localization = localization;
		this.receipt = receipt;
		this.alocable = alocable;
		this.available = available;
		this.type = type;
		this.predefined = predefined;
		this.macAdresses = new HashSet<String>();
		this.ipAdresses = new HashSet<String>();
		this.hostNames = new HashSet<String>();
	}
	
	public Equipment(String inventoryCode, String brand, String serialCode,
			Date acquisitionDate, String model, String localization,
			byte[] receipt, boolean alocable, boolean available, String type) {
		this(inventoryCode, brand, serialCode, acquisitionDate, model, localization, receipt, alocable, available, false, type);
	}
	
	public Equipment(String inventoryCode, String brand, String serialCode,
			Date acquisitionDate, String model, String localization,
			byte[] receipt, boolean alocable, boolean available, Class<? extends Equipment> clazz) {
		this(inventoryCode, brand, serialCode, acquisitionDate, model, localization, receipt, alocable, available, true, clazz.getSimpleName());
	}

	/**
	 * 
	 * @return
	 */
	public String getInventoryCode() {
		return inventoryCode;
	}

	/**
	 * 
	 * @return
	 */
	public String getBrand() {
		return brand;
	}

	/**
	 * 
	 * @return
	 */
	public String getSerialCode() {
		return serialCode;
	}

	/**
	 * 
	 * @return
	 */
	public Date getAcquisitionDate() {
		return acquisitionDate;
	}

	/**
	 * 
	 * @return
	 */
	public String getModel() {
		return model;
	}

	/**
	 * 
	 * @return
	 */
	public String getLocalization() {
		return localization;
	}

	/**
	 * 
	 * @return
	 */
	@Transient
	public byte[] getReceipt() {
		return receipt;
	}


	/**
	 * 
	 * @param inventoryCode
	 */
	protected void setInventoryCode(String inventoryCode) {
		this.inventoryCode = inventoryCode;
	}

	/**
	 * 
	 * @param brand
	 */
	protected void setBrand(String brand) {
		this.brand = brand;
	}

	/**
	 * 
	 * @param serialCode
	 */
	protected void setSerialCode(String serialCode) {
		this.serialCode = serialCode;
	}

	/**
	 * 
	 * @param aquisitionDate
	 */
	protected void setAcquisitionDate(Date aquisitionDate) {
		this.acquisitionDate = aquisitionDate;
	}

	/**
	 * 
	 * @param model
	 */
	protected void setModel(String model) {
		this.model = model;
	}

	protected boolean isPredefined() {
		return predefined;
	}


	protected void setPredefined(boolean predefined) {
		this.predefined = predefined;
	}


	/**
	 * 
	 * @param localization
	 */
	protected void setLocalization(String localization) {
		this.localization = localization;
	}

	/**
	 * 
	 * @param receipt
	 */
	protected void setReceipt(byte[] receipt) {
		this.receipt = receipt;
	}
	
	/**
	 * 
	 * @param receiptBlob
	 */
	@SuppressWarnings("unused")
	private void setReceiptBlob(Blob receiptBlob) {
		this.receipt = toByteArray(receiptBlob);
	}
	
	/**
	 * 
	 * @return
	 */
	@SuppressWarnings("unused")
	private Blob getReceiptBlob(){
		return Hibernate.createBlob(this.receipt);
	}


	/**
	 * 
	 * @return
	 */
	public boolean isAvailable() {
		return available;
	}

	/**
	 * 
	 * @param available
	 */
	protected void setAvailable(boolean available) {
		this.available = available;
	}
	
	/**
	 * 
	 * @return
	 */
	protected Set<String> getMacAdresses() {
		return macAdresses;
	}

	/**
	 * 
	 * @return
	 */
	protected Set<String> getIpAdresses() {
		return ipAdresses;
	}

	/**
	 * 
	 * @return
	 */
	protected Set<String> getHostNames() {
		return hostNames;
	}

	/**
	 * 
	 * @param macAdresses
	 */
	@SuppressWarnings("unused")
	private void setMacAdresses(Set<String> macAdresses) {
		this.macAdresses = macAdresses;
	}

	/**
	 * 
	 */
	@SuppressWarnings("unused")
	private void setIpAdresses(Set<String> ipAdresses) {
		this.ipAdresses = ipAdresses;
	}


	/**
	 * 
	 * @param hostNames
	 */
	@SuppressWarnings("unused")
	private void setHostNames(Set<String> hostNames) {
		this.hostNames = hostNames;
	}
	
	/**
	 * 
	 * @param ipAdress
	 */
	protected boolean addIPAdress(String ipAdress){
		return this.ipAdresses.add(ipAdress);
	}

	/**
	 * 
	 * @param hostname
	 */
	protected boolean addHostName(String hostname){
		return this.hostNames.add(hostname);
	}
	
	/**
	 * 
	 * @param macAddress
	 */
	protected boolean addMacAddress(String macAddress){
		return this.macAdresses.add(macAddress);
	}
	
	/**
	 * 
	 * @param ipAdress
	 */
	protected boolean removeIPAdress(String ipAdress){
		return this.ipAdresses.remove(ipAdress);
	}

	/**
	 * 
	 * @param hostname
	 */
	protected boolean removeHostName(String hostname){
		return this.hostNames.remove(hostname);
	}
	
	/**
	 * 
	 * @param macAddress
	 */
	protected boolean removeMacAddress(String macAddress){
		return this.macAdresses.remove(macAddress);
	}

	/**
	 * 
	 * @return
	 */
	public boolean isAlocable() {
		return alocable;
	}

	/**
	 * 
	 * @param alocable
	 */
	protected void setAlocable(boolean alocable) {
		this.alocable = alocable;
	}
	
	/**
	 * 
	 * @return
	 */
	public String getType() {
		return type;
	}

	/**
	 * 
	 * @param type
	 */
	protected void setType(String type) {
		this.type = type;
	}
	
	private byte[] toByteArray(Blob fromImageBlob) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		try {
			return toByteArrayImpl(fromImageBlob, baos);
		} catch (Exception e) {
			return null;
		}
	}

	private byte[] toByteArrayImpl(Blob fromImageBlob, ByteArrayOutputStream baos) throws SQLException, IOException {
		byte buf[] = new byte[4000];
		int dataSize;
		InputStream is = fromImageBlob.getBinaryStream(); 

		try {
			while((dataSize = is.read(buf)) != -1) {
				baos.write(buf, 0, dataSize);
			}    
		} finally {
			if(is != null) {
				is.close();
			}
		}
		return baos.toByteArray();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((acquisitionDate == null) ? 0 : acquisitionDate.hashCode());
		result = prime * result + (available ? 1231 : 1237);
		result = prime * result + ((brand == null) ? 0 : brand.hashCode());
		result = prime * result
				+ ((inventoryCode == null) ? 0 : inventoryCode.hashCode());
		result = prime * result
				+ ((localization == null) ? 0 : localization.hashCode());
		result = prime * result + ((model == null) ? 0 : model.hashCode());
		result = prime * result + Arrays.hashCode(receipt);
		result = prime * result
				+ ((serialCode == null) ? 0 : serialCode.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Equipment other = (Equipment) obj;
		if (acquisitionDate == null) {
			if (other.acquisitionDate != null)
				return false;
		} else if (acquisitionDate.getTime() == other.acquisitionDate.getTime())
			return false;
		if (available != other.available)
			return false;
		if (brand == null) {
			if (other.brand != null)
				return false;
		} else if (!brand.equals(other.brand))
			return false;
		if (inventoryCode == null) {
			if (other.inventoryCode != null)
				return false;
		} else if (!inventoryCode.equals(other.inventoryCode))
			return false;
		if (localization == null) {
			if (other.localization != null)
				return false;
		} else if (!localization.equals(other.localization))
			return false;
		if (model == null) {
			if (other.model != null)
				return false;
		} else if (!model.equals(other.model))
			return false;
		if (!Arrays.equals(receipt, other.receipt))
			return false;
		if (serialCode == null) {
			if (other.serialCode != null)
				return false;
		} else if (!serialCode.equals(other.serialCode))
			return false;
		return true;
	}
}
